﻿#include <cstdio>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>         //exit()
#include <sys/wait.h>       //wait,waitpid
#include <sys/signal.h>     //sigaction
        
//TCPServer,阻塞方式
const int SERVER_PORT = 5588;       //服务器端口

//用于处理僵死进程的回调函数
void HandleZProcess(int signo);

int main()
{
    printf("阻塞式TCP服务器，多个进程支持多客户端连接，端口号:%d!\n",SERVER_PORT);
    int server_sock_fd = 0;             //监听套接字
    int clientFd = 0;                   //客户端连接套接字
    struct sockaddr_in serverAddr;      //server地址
    bzero(&serverAddr, 0);
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    pid_t pid = 0;                      //子进程的PID

    //step1:创建tcp服务,成功，返回套接字句柄，失败返回-1并设置errno
    if ((server_sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        //如何获取失败信息
        perror("socket创建失败");
        return -1;
    }
    else {
        printf("socket创建成功\n");
    }

    //step2:绑定套接口和套接字
    int bindRet = bind(server_sock_fd, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if ( bindRet < 0)
    {
        close(server_sock_fd);
        perror("绑定到套接口错误");
        return -1;
    }

    //setp3:监听套接字，设置最大连接缓冲队列（已连接+未连接）
    if (listen(server_sock_fd, 100) < 0)
    {
        close(server_sock_fd);
        perror("监听失败");
        return -1;
    }

    //添加对僵死子进程的处理
    struct sigaction newact,oldact;
    newact.sa_handler = &HandleZProcess;
    sigemptyset(&newact.sa_mask);
    //sigaddset(&newact.sa_mask, SIGQUIT);
    newact.sa_flags = 0;
    sigaction(SIGCHLD, &newact, &oldact);

    //step4:获取响应连接
    for (;;)
    {
        sockaddr_in clientAddr;             //保存客户端地址信息
        socklen_t clientAddrLen = sizeof(clientAddr);
        if ((clientFd = accept(server_sock_fd, (sockaddr*)&clientAddr, &clientAddrLen)) < 0)
        {
            if (errno == EINTR) //errno是EINTR的情况下可以重启accept系统调用，其它情况不可以
                continue;
            else
            {
                printf("响应客户端失败，错误码[%d]", errno);
                return -1;
            }            
        }
        else
        {
            if ((pid = fork()) == 0)
            {
                close(server_sock_fd);              //关闭监听套接字
                //子进程
                char clientIP[32] = { 0x00 };        //IP地址
                int clientPort = ntohs(clientAddr.sin_port);
                printf("客户端连接（子进程），IP地址[%s],端口号[%d]\n"
                    , inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP))
                    , ntohs(clientAddr.sin_port));

                //开始和客户端通信               
                char recvBuf[1024] = { 0x00 };
                char sendBuf[1024] = { 0x00 };

             //   printf("客户端[%s:%d]\n", clientIP, clientPort);
                while (true)
                {
                    int readlen = read(clientFd, recvBuf, sizeof(recvBuf));
                    if (readlen > 0)
                    {
                        char* s = new char[readlen + 1];
                        bzero(s, 0);
                        bcopy(recvBuf, s, readlen);
                        printf("客户端[%s:%d],接收数据:%s\n", clientIP, clientPort, s);
                        delete[]s;
                        write(clientFd, recvBuf, sizeof(recvBuf));
                    }
                    else
                    {
                        //=0判定为客户端关闭
                        printf("客户端[%s:%d]关闭连接\n", clientIP, clientPort);
                        close(clientFd);
                        exit(0);        //关闭子进程
                        break;
                    }
                }
            }
            else
            {
                printf("调用子进程，进程号:%d\n", pid);
                close(clientFd);
            }
               
        }
    }
   
    //关闭服务
    close(server_sock_fd);

    return 0;
}


void HandleZProcess(int signo)
{
    pid_t pid;
    int stat;

    while((pid = waitpid(-1,&stat,WNOHANG)) > 0)
        printf("子进程[%d] 结束\n", pid);
    return;
}